﻿#region Copyright

//  ======================================================
//      Copyright (c) 2011 Toulr All rights reserved.
//     
//      The use and distribution terms for this software are contained in the file
//      named license.txt, which can be found in the root of this distribution.
//      By using this software in any fashion, you are agreeing to be bound by the
//      terms of this license.
//     
//     You must not remove this notice, or any other, from this software.
//  ======================================================

#endregion

#region Using Namespaces

using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Objects.DataClasses;
using System.Reflection;
using System.Threading;
using System;

#endregion

namespace Toulr.Domains
{
    public class Repository<TEntity> : IRepository<TEntity> where TEntity : class, IEntity
    {
        public DomainContext DbContext = DomainContext.Instance;

        private DbSet<TEntity> DbSet
        {
            get { return DbContext.Set<TEntity>(); }
        }

        #region IRepository<TEntity> Members

        public IEnumerable<TEntity> FindAll()
        {
            return DbSet;
        }

        public TEntity Add(TEntity entity)
        {
            TEntity e = DbSet.Add(entity);
            DbContext.SaveChanges();
            return e;
        }

        public void Delete(TEntity entity)
        {
            DbContext.Dispose();
            DbContext = DomainContext.Instance;
            DbContext.Entry(entity).State = EntityState.Deleted;
            DbContext.SaveChanges();
        }

        public TEntity Update(TEntity entity)
        {
            DbContext.Dispose();
            DbContext = DomainContext.Instance;
            DbContext.Entry(entity).State = EntityState.Modified;
            DbContext.SaveChanges();
            return entity;
        }
        /// <summary>
        /// 更新对实体象中发生变化的属性
        /// </summary>
        /// <param name="entityNew"></param>
        /// <param name="entityOrig"></param>
        /// hyyu 20110515
        public void Update(TEntity entityNew, TEntity entityOrig)
        {
            if (entityNew == null)
                throw new Exception("entityNew is null");
            if (entityOrig == null)
            {
                Update(entityOrig);
                return;
            }

            DbContext.Dispose();
            DbContext = DomainContext.Instance;
            DbContext.Entry(entityNew).State = EntityState.Modified;

            DbSet.Attach(entityNew);
            var stateEntry =
              ((IObjectContextAdapter)DbContext).ObjectContext.ObjectStateManager.GetObjectStateEntry(entityNew);

            var flag = false;

            for (int i = 0; i < entityNew.GetType().GetProperties().Length; i++)
            {
                if (!EqualsProp(entityNew,entityNew.GetType().GetProperties()[i],entityOrig,entityOrig.GetType().GetProperties()[i]))
                {
                    stateEntry.SetModifiedProperty(entityNew.GetType().GetProperties()[i].Name);
                    flag = true;
                }
            }

            if(!flag)
                return;

            DbContext.SaveChanges();

        }

        /// <summary>
        /// 比较两个对象的属性值是否相等
        /// </summary>
        /// <param name="obj1"></param>
        /// <param name="propertyInfo1"></param>
        /// <param name="obj2"></param>
        /// <param name="propertyInfo2"></param>
        /// <returns></returns>
        private static bool EqualsProp(object obj1, PropertyInfo propertyInfo1, object obj2, PropertyInfo propertyInfo2)
        {
            if (propertyInfo1.Name != propertyInfo2.Name)
                return false;
            if (propertyInfo1.PropertyType.FullName != propertyInfo2.PropertyType.FullName)
                return false;
            if ((propertyInfo1.GetValue(obj1, null) == null && propertyInfo2.GetValue(obj2, null) != null) || (propertyInfo2.GetValue(obj2, null) == null && propertyInfo1.GetValue(obj1, null) != null))
                return false;
            if ((propertyInfo1.GetValue(obj1, null) == null && propertyInfo2.GetValue(obj2, null) != null))
                return false;

            if (propertyInfo1.PropertyType.IsValueType || propertyInfo1.PropertyType.FullName == "System.String")
            {
                if (propertyInfo1.GetValue(obj1, null).Equals(propertyInfo2.GetValue(obj2, null)))
                    return true;
                return false;
            }

            if (propertyInfo1.GetValue(obj1, null) is ICollection)
            {
                return EqualsCollection(propertyInfo1.GetValue(obj1, null), propertyInfo1.GetValue(obj2, null));
            }

            if (propertyInfo1.PropertyType.IsClass)
            {
                for (int i = 0; i < propertyInfo1.GetType().GetProperties().Length; i++)
                {
                    if (
                        !EqualsProp(propertyInfo1.GetValue(obj1, null),
                                    propertyInfo1.GetValue(obj1, null).GetType().GetProperties()[i],
                                    propertyInfo2.GetValue(obj2, null),
                                    propertyInfo1.GetValue(obj1, null).GetType().GetProperties()[i]))
                        return false;
                }
            }
            return true;
        }

        /// <summary>
        /// 比较两个集合或数组是否相等（长度 元素的值）
        /// </summary>
        /// <param name="obj1"></param>
        /// <param name="obj2"></param>
        /// <returns></returns>
        private static bool EqualsCollection(object obj1, object obj2)
        {
            //ToDo 暂不支持key value的集合

            var objects1 = new object[] { };
            var objects2 = new object[] { };

            if (obj1 is ICollection)
            {
                objects1 = new object[((ICollection)(obj1)).Count];
                ((ICollection)(obj1)).CopyTo(objects1, 0);
                objects2 = new object[((ICollection)(obj2)).Count];
                ((ICollection)(obj2)).CopyTo(objects2, 0);
            }
            if (objects1.Length != objects2.Length)
                return false;

            for (int i = 0; i < objects1.Length; i++)
            {
                if (objects1[0].GetType().IsValueType || objects1[0].GetType().FullName == "System.String")
                    if (objects1[0].Equals(objects2[0]))
                        return true;
                    else
                        return false;
                if (objects1[0].GetType().IsClass)
                    for (int j = 0; j < objects1[0].GetType().GetProperties().Length; j++)
                    {
                        if (!EqualsProp(objects1[i], objects1[i].GetType().GetProperties()[j], objects2[i],
                                        objects2[i].GetType().GetProperties()[j]))
                            return false;
                    }
                if (objects1[0] is ICollection)
                    for (int j = 0; j < objects1.Length; j++)
                    {
                        if (!EqualsCollection(objects1[0], objects2[0]))
                            return false;
                    }
            }

            return true;
        }

        #endregion
    }
}